package com.hank.util;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.hank.dto.UserDTO;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.crypto.SecretKey;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author gaoZhiHao
 * @date 2023/5/11 14:20
 */
@Component
public class JwtUtils {

    @Value("${jwt.secret}")
    private String SECRET_KEY;

    @Value("${jwt.expiration}")
    private Long expiration;
    // Redis前缀
    private static final String PREFIX = "token:";

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 生成JWT token
     */
    public String generateToken(UserDTO userDTO) {
        // 生成密钥
        SecretKey secretKey = Keys.hmacShaKeyFor(SECRET_KEY.getBytes());

        // 构造JWT builder
        // 设置自定义声明
        Map<String, Object> claims = objectToMap(userDTO);
        JwtBuilder builder = Jwts.builder().setClaims(claims)
                // 设置签发时间
                .setIssuedAt(new Date())
                // 设置过期时间
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 10000))
                // 签名算法
                .signWith(secretKey, SignatureAlgorithm.HS256);
        String token = builder.compact();
        redisTemplate.opsForValue().set(PREFIX + token, JSON.toJSONString(claims));
        redisTemplate.expire(token, expiration, TimeUnit.SECONDS);
        // 构造JWT token
        return token;
    }

    /**
     * 解析JWT token
     */
    public UserDTO parseToken(String token) {

        // 解析密钥
        SecretKey secretKey = Keys.hmacShaKeyFor(SECRET_KEY.getBytes());
        Jws<Claims> claimsJws = null;

        try {
            // 解析JWT token
            claimsJws = Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token);
        } catch (ExpiredJwtException e) {
            throw new RuntimeException("token已经过期");
        }
        Claims body = claimsJws.getBody();
        // 创建 UserDTO 对象，并从 JSONObject 中获取需要的属性值
        UserDTO userDTO = new UserDTO();
        userDTO.setId(body.get("id",Long.class));
        userDTO.setName(body.get("name",String.class));
        String key = PREFIX + token;
        String value = null;
        try {
            value = redisTemplate.opsForValue().get(key).toString();

            if (userDTO.equals(JSONObject.parseObject(value, UserDTO.class))) {
                return userDTO;
            } else {
                // JWT token无效
                throw new JwtException("Invalid token");
            }
        } catch (JwtException e) {
            throw new RuntimeException("token被篡改");
        } catch (RuntimeException e) {
            throw new RuntimeException("token已经过期");
        }
    }

    public static Map<String, Object> objectToMap(Object obj) {
        if (obj == null) {
            return null;
        }

        Map<String, Object> map = new HashMap<>();

        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                map.put(field.getName(), field.get(obj));
            } catch (IllegalAccessException e) {
                // Handle exception here
            }
        }

        return map;
    }
}
